﻿#include "FITKAcousticsVTKGraphObjectSEAShell.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>

// App
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysAbsShell.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEAFace.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEASubSysGeoProp.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsTriangles.h"
#include "FITK_Component/FITKAcousticsPhysics/FITKAcousticsAbsNCT.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractElement.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEAShell::FITKAcousticsVTKGraphObjectSEAShell(Acoustics::FITKAbstractSEASubSysShell* shell)
        : FITKAcousticsVTKGraphObjectSEASubSystem(shell)
    {
        // Return if the input is nullptr.
        if (!shell)
        {
            return;
        }

        // Intiailize the polygon offset.
        m_polygonOffset = -3.;

        // Initialize vtk objects.
        init();
        
        // Generate the data set.
        if (!generateDataSet(shell))
        {
            return;
        }

        // Generate the nodes actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();

        // Save the data type.
        m_graphInfo.Type = FITKAcousticsVTKCommons::ShellGraph;
    }

    FITKAcousticsVTKGraphObjectSEAShell::~FITKAcousticsVTKGraphObjectSEAShell()
    {
        // Delete vtk objects.
        deleteVtkObj(m_ugridBounds);
    }

    void FITKAcousticsVTKGraphObjectSEAShell::init()
    {
        this->Superclass::init();

        // Initialize the bounds grid.
        m_ugridBounds = vtkUnstructuredGrid::New();
        m_ugridBounds->SetPoints(m_points);
    }

    void FITKAcousticsVTKGraphObjectSEAShell::updateVisibility()
    {
        Acoustics::FITKAbstractSEASubSysShell* shell = dynamic_cast<Acoustics::FITKAbstractSEASubSysShell*>(_dataObj);
        if (!shell)
        {
            return;
        }

        // Get the runtime settings for visibility.
        bool visibility = true;
        Acoustics::FITKAbstractSEASubSys::SEASubSysType ssType = shell->getSEASubSysType();
        switch (ssType)
        {
        case Acoustics::FITKAbstractSEASubSys::SEAFlatPlate: 
            visibility = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_FLAT_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEASinglyCurvedShell:
            visibility = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_S_CUR_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEADoublyCurvedShell:  
            visibility = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_D_CUR_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEACylinderShell:
            visibility = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_CYLINDER_PLATE, true);
            break;
        default:
            return;
        }

        bool mainFlag = this->Superclass::getDataVisibility() && visibility;

        // Update the visibility with the flags.
        //@{
        // Show or hide actors.
        setVisible(mainFlag);
        //@}
    }

    bool FITKAcousticsVTKGraphObjectSEAShell::getDataVisibility()
    {
        Acoustics::FITKAbstractSEASubSysShell* shell = dynamic_cast<Acoustics::FITKAbstractSEASubSysShell*>(_dataObj);
        if (!shell)
        {
            return false;
        }

        // Get the runtime settings for visibility.
        Acoustics::FITKAbstractSEASubSys::SEASubSysType ssType = shell->getSEASubSysType();
        bool showSEASubSys = this->Superclass::getDataVisibility();

        bool showShell = true;
        switch (ssType)
        {
        case Acoustics::FITKAbstractSEASubSys::SEAFlatPlate:
            showShell = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_FLAT_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEASinglyCurvedShell:
            showShell = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_S_CUR_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEADoublyCurvedShell:
            showShell = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_D_CUR_PLATE, true);
            break;
        case Acoustics::FITKAbstractSEASubSys::SEACylinderShell:
            showShell = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS_CYLINDER_PLATE, true);
            break;
        default:
            return false;
        }

        return showShell && showSEASubSys;
    }

    bool FITKAcousticsVTKGraphObjectSEAShell::generateDataSet(Acoustics::FITKAbstractSEASubSys* subSys)
    {
        Acoustics::FITKAbstractSEASubSysShell* shell = dynamic_cast<Acoustics::FITKAbstractSEASubSysShell*>(subSys);
        if (!shell || !m_points || !m_ugrid)
        {
            return false;
        }

        // Get the SEA nodes.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = getSEANodesData();
        if (!seaNodes)
        {
            return false;
        }

        // Generate points.
        generateSEANodes();

        // Get all triangles.
        Acoustics::FITKAcousticsTriangles* tris = shell->getTriangleMesh();
        if (!tris)
        {
            return false;
        }

        int nTris = tris->getElementCount();
        for (int i = 0; i < nTris; i++)
        {
            Interface::FITKAbstractElement* tri = tris->getElementAt(i);
            if (!tri)
            {
                continue;
            }

            // Check the triangle.
            int nNodes = tri->getNodeCount();
            if (nNodes != 3)
            {
                continue;
            }

            // Get the node ids.
            int nodeId_1 = tri->getNodeID(0);
            int nodeId_2 = tri->getNodeID(1);
            int nodeId_3 = tri->getNodeID(2);

            // Get the node indice.
            int ptIndex_1 = m_nodeIdPointIndexHash.value(nodeId_1, -1);
            int ptIndex_2 = m_nodeIdPointIndexHash.value(nodeId_2, -1);
            int ptIndex_3 = m_nodeIdPointIndexHash.value(nodeId_3, -1);

            if (ptIndex_1 == -1 || ptIndex_2 == -1 || ptIndex_3 == -1)
            {
                qDebug() << QString("Invalid node id in SEA sub-systems!");
                qDebug() << QString("Triangle node ids: %1, %2, %3.").arg(nodeId_1).arg(nodeId_2).arg(nodeId_3);
                qDebug() << QString("Triangle node indice in VTK: %1, %2, %3.").arg(ptIndex_1).arg(ptIndex_2).arg(ptIndex_3);
                continue;
            }

            // Add a triangle cell.
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptIndex_1);
            cell->InsertNextId(ptIndex_2);
            cell->InsertNextId(ptIndex_3);
            m_ugrid->InsertNextCell(VTKCellType::VTK_TRIANGLE, cell);
        }

        // Get the geometry properties from the sub-system.
        Acoustics::FITKSEASubSysGeoProp* shellProp = shell->getGeoProp();
        if (shellProp)
        {
            // Generate the boundary edges cells.
            QList<QPair<int, int>> edges = shellProp->getBoundary();

            for (const QPair<int, int> & edge : edges)
            {
                int ptIndex_1 = m_nodeIdPointIndexHash[edge.first];
                int ptIndex_2 = m_nodeIdPointIndexHash[edge.second];

                // Create the line cell.
                vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
                cell->InsertNextId(ptIndex_1);
                cell->InsertNextId(ptIndex_2);
                m_ugridBounds->InsertNextCell(VTKCellType::VTK_LINE, cell);
            }
        }

        // Generate the scaled points with the sub-system center.
        double* bds = m_points->GetBounds();
        double center3[3]{ (bds[0] + bds[1]) * 0.5, 
            (bds[2] + bds[3]) * 0.5, (bds[4] + bds[5]) * 0.5 };
        generateScaledSEANodes(center3);

        return true;
    }

    void FITKAcousticsVTKGraphObjectSEAShell::generateGraph()
    {
        // Generate the actor.
        FITKAcousticsVTKGraphObjectSEASubSystem::generateGraph();

        m_fActor->setActorType(ActorType::SurfaceActor);
        m_fActor->GetProperty()->SetEdgeVisibility(false);

        // Initialize color.
        double color3[3]{ 0., 0., 0. };
        FITKAcousticsVTKCommons::QColorToDouble3(FITKAcousticsVTKCommons::s_SEASubSysShellColor, color3);
        m_fActor->GetProperty()->SetColor(color3);

        // Create bounds actor.
        m_fActorBounds = FITKAcousticsVTKGraphActor::New();
        m_fActorBounds->setGraphObject(this);
        m_fActorBounds->setActorType(ActorType::EdgeActor);
        m_fActorBounds->SetPickable(false);
        m_fActorBounds->SetVisibility(true);
        m_fActorBounds->GetProperty()->SetRepresentation(1);
        m_fActorBounds->GetProperty()->SetPointSize(0.);
        m_fActorBounds->GetProperty()->SetLineWidth(1.);
        m_fActorBounds->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        m_fActorBounds->GetProperty()->SetColor(0., 0., 0.);

        // Initialize the input data.
        m_fActorBounds->setInputDataObject(m_ugridBounds);

        addActor(m_fActorBounds);
    }

    double FITKAcousticsVTKGraphObjectSEAShell::getScaleFactor()
    {
        return FITKAcousticsVTKCommons::s_flatShellScaleFactor;
    }

    void FITKAcousticsVTKGraphObjectSEAShell::resetVtkData()
    {
        FITKAcousticsVTKGraphObjectSEASubSystem::resetVtkData();

        resetVtkObj(m_ugridBounds);
    }
}   // namespace Exchange